---
title: 动态SQL
---

动态SQL就是指根据不同的条件生成不同的SQL语句。

共有以下四种标签：

* if
* choose(when, otherwise)
* trim(where, set)
* foreach

## 搭建环境

```sql
CREATE TABLE `blog`
(
    id          VARCHAR(50)  NOT NULL COMMENT '博客id',
    title       VARCHAR(100) NOT NULL COMMENT '博客标题',
    author      VARCHAR(30)  NOT NULL COMMENT '作者',
    create_time datetime     NOT NULL COMMENT '创建时间',
    views       int(30) NOT NULL COMMENT '浏览量'
)ENGINE = INNODB DEFAULT CHARSET=utf8;
```

## IF

```xml
<!--查询博客-->
<select id="queryBlogIf" resultType="pub.wkq.pojo.Blog">
    select * from blog
    <where>
        <if test="title != null">
            and title like concat('%',#{title},'%')
        </if>
        <if test="author != null">
            and author like concat('%',#{author},'%')
        </if>
    </where>
</select>
```

## choose、when、otherwise

类似 Java 中的 switch 语句

    ```xml
    <!--查询博客-->
        <select id="queryBlogChoose" resultType="pub.wkq.pojo.Blog">
            select * from blog
            <where>
                <choose>
                    <when test="title != null">
                        and title like concat('%',#{title},'%')
                    </when>
                    <when test="author != null">
                        and author like concat('%',#{author},'%')
                    </when>
                    <otherwise>
                        and views = #{views}
                    </otherwise>
                </choose>
            </where>
        </select>
    ```

## trim、where、set

where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且，若语句的开头为“AND”或“OR”，where
元素也会将它们去除。

set 元素会动态前置 SET 关键字，同时也会删除无关的逗号。用于实现动态更新。

```xml
<!--更新博客-->
<update id="updateBlog">
    update mybatis.blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author},
        </if>
        <if test="createTime != null">
            create_time = #{createTime},
        </if>
        <if test="views != null">
            views = #{views},
        </if>
    </set>
    where id = #{id}
</update>
```

set 和 where 都是 trim 标签的特殊用法，trim 标签可以自定义前缀和后缀，用于实现动态 SQL。

```xml title='通过自定义 trim 元素来定制 where'

<trim prefix="WHERE" prefixOverrides="OR |AND">
    <choose>
        <when test="title != null and title != ''">
            and title like concat('%',#{title},'%')
        </when>
        <when test="author != null and author != ''">
            and author like concat('%',#{author},'%')
        </when>
        <otherwise>
            and views &gt; 1000
        </otherwise>
    </choose>
</trim>
```

```xml title='通过自定义 trim 元素来定制 set'

<update id="updateBlog">
    update mybatis.blog
    <trim prefix="SET" suffixOverrides=",">
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author},
        </if>
        <if test="createTime != null">
            create_time = #{createTime},
        </if>
        <if test="views != null">
            views = #{views},
        </if>
    </trim>
    where id = #{id}
</update>
```

trim 标签的属性：

* prefix：前缀
* prefixOverrides：去除前缀,多个前缀用 | 分割,如：prefixOverrides="OR |AND"，表示去除 OR 和 AND。
* suffix：后缀
* suffixOverrides：去除后缀，多个后缀用 | 分割，如：suffixOverrides=","，表示去除最后一个逗号。

prefix 和 suffix 表示插入属性设置中的内容。

:::tip
所谓的动态 SQL，本质还是 SQL 语句，只是我们可以在 SQL 层面，去执行逻辑代码。
:::

## foreach
foreach 功能非常强大，它允许你指定一个集合，声明可以在元素体内使用的集合项（item） 和索引（index）变量。它也允许你指定开头和结尾的字符串，以及在集合元素之间放置分隔符。

当使用 Map 对象时，index 属性是键，item 属性是值。

```xml

<select id="queryBlogForeach" resultType="pub.wkq.pojo.Blog">
    select * from blog
    <where>
        <foreach collection="ids" item="id" open="and id in (" close=")" separator=",">
            #{id}
        </foreach>
    </where>
</select>
```

## sql片段

有的时候，我们会将一些公共的部分抽取出来，方便复用。

最好基于单表来定义 SQL 片段，不要存在 where 标签。